
goog.provide('esn.Question');

goog.require('esn.Slider');
goog.require('esn.dao.AnswerDao');
goog.require('esn.model.AnswerModel');
goog.require('esn.soy.survey');
goog.require('goog.i18n.DateTimeFormat');
goog.require('goog.i18n.DateTimeParse');
goog.require('goog.string');
goog.require('goog.ui.Component');
goog.require('goog.ui.InputDatePicker');
goog.require('goog.ui.LabelInput');
goog.require('soy');


/**
 * @param {esn.AppContext} appContext
 * @param {esn.model.QuestionMdoel} model A question model.
 * @param {number} orderNumber
 * @param {esn.model.AnswerModel} opt_answerModel
 * @constructor
 */
esn.Question = function(appContext, model, orderNumber,  opt_answerModel,
    opt_domHelper) {
  goog.base(this, opt_domHelper);

  this.model_ = model;

  this.surveyClient_ = appContext.getService(
      esn.constants.ServiceId.SURVEY_CLIENT);

  this.orderNumber_ = orderNumber;

  var answerModel = opt_answerModel || new esn.model.AnswerModel(this.model_);
  this.answerDao_ = new esn.dao.AnswerDao(answerModel, this.surveyClient_,
      2000);

  this.enabled_ = true;
};
goog.inherits(esn.Question, goog.ui.Component);

esn.Question.EventType = {
  CHANGE: goog.events.getUniqueId('change')
};

esn.Question.SLIDER_POSSIBLE_VALUES_ = [
  '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14',
  '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25'
];

// Global date formatter for all questions.
esn.Question.dateFormatter_;

// Global date parser for all questions.
esn.Question.dateParser_;

esn.Question.prototype.formEls_;

esn.Question.prototype.slider_;

esn.Question.prototype.inputDatePicker_;

esn.Question.prototype.notApplicableCheckbox_;

esn.Question.prototype.createDom = function() {
  goog.base(this, 'createDom');

  if (this.model_.getType() == esn.constants.QuestionType.SLIDER) {
    this.setElementInternal(soy.renderAsElement(esn.soy.survey.sliderQuestion,
        {Question: this.model_.toObject(), Number: this.orderNumber_}));
  } else {
    this.setElementInternal(soy.renderAsElement(esn.soy.survey.question,
        {Question: this.model_.toObject(), Number: this.orderNumber_}));
  }

  this.decorateInternal(this.getElement());
};

esn.Question.prototype.decorateInternal = function(element) {
  goog.base(this, 'decorateInternal', element);

  this.formEls_ = this.getElement().getElementsByTagName('input');

  switch (this.model_.getType()) {
    case esn.constants.QuestionType.SLIDER:
      // Render slider.
      this.slider_ = new esn.Slider(this.model_.getPossibleAnswers(),
          this.getElementByClass('sliderValue'));
      this.addChild(this.slider_);
      this.slider_.decorate(this.getElementByClass('slider'));
      break;
    case esn.constants.QuestionType.DATE:
      goog.asserts.assert(this.formEls_.length == 1,
          'Date must have exactly one input field');

      // Render date picker.      
      var labelInput = new goog.ui.LabelInput('mm/dd/yyyy');
      labelInput.decorate(this.formEls_[0]);
      // TODO(ncabrera): Uncomment this once we figure out why the alignment is
      // off!
      // this.inputDatePicker_ = new goog.ui.InputDatePicker(
      //     this.getDateFormatter_(),
      //     this.getDateParser_());
      // this.addChild(this.inputDatePicker_);
      // this.inputDatePicker_.decorate(this.formEls_[0]);
      break;
  }
};

esn.Question.prototype.enterDocument = function() {
  goog.base(this, 'enterDocument');

  goog.array.forEach(this.formEls_, function(formEl) {
    switch (formEl.type) {
      case 'text':
        this.getHandler().listen(
            formEl,
            goog.events.EventType.BLUR,
            this.handleInputChange_);
        break;
      case 'checkbox':
        this.getHandler().listen(
            formEl,
            goog.events.EventType.CLICK,
            this.handleCheckboxChange_);
        break;
      case 'radio':
      default:
        this.getHandler().listen(
            formEl,
            goog.events.EventType.CLICK,
            this.handleRadioChange_);
    }
  }, this);

  if (this.slider_) {
    this.getHandler().listen(
        this.slider_,
        esn.Slider.EventType.CHANGE,
        this.handleSliderChange_);
  }

  if (this.answerDao_.getModel().isSet()) {
    this.setValues(this.answerDao_.getModel().getValues());
  }
};

esn.Question.prototype.getDateFormatter_ = function() {
  if (!esn.Question.dateFormatter_) {
    esn.Question.dateFormatter_ = new goog.i18n.DateTimeFormat('MM/dd/yyyy');
  }
  return esn.Question.dateFormatter_;
};

esn.Question.prototype.getDateParser_ = function() {
  if (!esn.Question.dateParser_) {
    esn.Question.dateParser_ = new goog.i18n.DateTimeParse('MM/dd/yyyy');
  }
  return esn.Question.dateParser_;
};

esn.Question.prototype.isAnswered = function() {
  return this.answerDao_.getModel().isSet();
};

esn.Question.prototype.highlightAsUnanswered = function() {
  // Highligh any unaswered questions.
  goog.dom.classes.add(this.getElement(), 'missing');

  // If this is an INPUT or INPUT_INLINE question then add an N/A checkbox.
  if (!this.notApplicableCheckbox_ &&
      (this.model_.getType() == esn.constants.QuestionType.INPUT ||
       this.model_.getType() == esn.constants.QuestionType.DATE ||
       this.model_.getType() == esn.constants.QuestionType.INPUT_INLINE)) {
    goog.dom.appendChild(this.getElement(),
        soy.renderAsElement(esn.soy.survey.notApplicableCheckbox,
            {Question: this.model_.toObject()}));

    // Listen for a checkbox click.
    this.notApplicableCheckbox_ =
        goog.dom.getElement('notApplicableCheckbox-' + this.model_.getId());
    this.getHandler().listen(
        this.notApplicableCheckbox_,
        goog.events.EventType.CLICK,
        this.handleNACheckboxClick_);
    
  }
};

esn.Question.prototype.setEnabled_ = function(enabled) {
  this.enabled_ = enabled;
  if (this.getElement()) {
    goog.array.forEach(this.formEls_, function(formEl) {
      // The N/A field is controlled on it's own so don't disable it.
      if (formEl != this.notApplicableCheckbox_) {
        formEl.disabled = !enabled;
      }
    }, this);
  }
};

esn.Question.prototype.setValues = function(values) {
  if (!this.getElement()) {
    throw new Error('Question must be rendered before a value can be set.');
  }
  
  switch (this.model_.getType()) {
    case esn.constants.QuestionType.RADIO:
      this.setValuesForRadioQuestion_(values);
      break;
    case esn.constants.QuestionType.CHECKBOX:
      this.setValuesForCheckboxQuestion_(values);
      break;
    case esn.constants.QuestionType.DATE:
    case esn.constants.QuestionType.INPUT:
    case esn.constants.QuestionType.INPUT_INLINE:
      this.setValuesForInputQuestion_(values);
      break;
    case esn.constants.QuestionType.SLIDER:
      this.slider_.setValue(parseInt(values[0], 10));
      break;
  }
};

esn.Question.prototype.setValuesForInputQuestion_ = function(values) {
  goog.array.forEach(values, function(value, index) {
    if (this.formEls_.length > index) {
      this.formEls_[index].value = value;
    }
  }, this)
};

esn.Question.prototype.setValuesForRadioQuestion_ = function(values) {
  var value = values.length ? values[0] : '';
  if (goog.string.startsWith(value, esn.constants.OTHER_VALUE_PREFIX)) {
    this.setOtherInput_(value);
  } else {
    var radioEl = goog.array.find(this.formEls_, function(formEl) {
      return formEl.type == 'radio' && formEl.value == value;
    }, this);
    if (radioEl) {
      radioEl.checked = true;
    }
  }
};

esn.Question.prototype.setValuesForCheckboxQuestion_ = function(values) {
  goog.array.forEach(this.formEls_, function(formEl) {
    if (goog.array.contains(values, formEl.value)) {
      formEl.checked = true;
    }
  }, this);

  // Set this "Other" field if it's set in our values.
  var otherValue = goog.array.find(values, function(value) {
    return goog.string.startsWith(value, esn.constants.OTHER_VALUE_PREFIX);
  });
  if (otherValue) {
    this.setOtherInput_(otherValue);
  }
};

esn.Question.prototype.setOtherInput_ = function(value) {
  var otherInputEl = this.getOtherInputEl_();
  if (otherInputEl) {
    otherInputEl.value = value.replace(esn.constants.OTHER_VALUE_PREFIX, '');
    var otherCheckboxOrRadioEl = this.getOtherRadioOrCheckbox_();
    if (otherCheckboxOrRadioEl) {
      otherCheckboxOrRadioEl.checked = true;
    }
  }
};

esn.Question.prototype.handleInputChange_ = function(e) {
  var value = goog.string.trim(e.target.value);
  var values = value ? [value] : [];
  if (this.model_.getType() == esn.constants.QuestionType.CHECKBOX) {
    values = this.getCheckboxValues_();
    // Check or uncheck the "Other" checkbox based on the value.
    var otherCheckboxEl = this.getOtherRadioOrCheckbox_();
    if (otherCheckboxEl) {
      otherCheckboxEl.checked = !goog.string.isEmpty(value);
    }
  } else if (this.model_.isMultiInput()) {
    values = this.getMultiInputValues_();
  } else if (this.model_.getType() == esn.constants.QuestionType.RADIO &&
      values.length > 0) {
    values[0] = esn.constants.OTHER_VALUE_PREFIX + values[0];
    // Check the "Other" radio button based.
    var otherRadioEl = this.getOtherRadioOrCheckbox_();
    if (otherRadioEl) {
      otherRadioEl.checked = true;
    }
  }

  this.saveChanges_(values);
};

esn.Question.prototype.handleCheckboxChange_ = function(e) {
  // If the "Other" checkbox we unchecked them clear the "Other" input element.
  if (e.target.value == esn.constants.AnswerType.OTHER && !e.target.checked) {
    var otherInputEl = this.getOtherInputEl_();
    if (otherInputEl) {
      otherInputEl.value = '';
    }
  }
  this.saveChanges_(this.getCheckboxValues_());
};

esn.Question.prototype.handleRadioChange_ = function(e) {
  var value = e.target.value;
  var otherInputEl = this.getOtherInputEl_();
  if (value == esn.constants.AnswerType.OTHER) {
    if (otherInputEl && otherInputEl.value != '') {
      value = 'Other: ' + otherInputEl.value;
    } else {
      value = '';
    }
  } else if (otherInputEl) {
    // Clear "Other" input element.
    otherInputEl.value = '';
  }
  this.saveChanges_([value]);
};

esn.Question.prototype.handleSliderChange_ = function(e) {
  var value = String(e.target.getValue());
  this.saveChanges_([value]);
};

esn.Question.prototype.handleNACheckboxClick_ = function(e) {
  var values = e.target.checked ? [e.target.value] : [];
  this.setEnabled_(!e.target.checked);
  this.saveChanges_(values);
};

esn.Question.prototype.getMultiInputValues_ = function() {
  var values = [];
  goog.array.forEach(this.formEls_, function(formEl) {
    if (formEl.type == 'text' && formEl.value != '') {
      values.push(formEl.value);
    }
  }, this);
  return values;
};

esn.Question.prototype.getCheckboxValues_ = function() {
  var values = [];
  goog.array.forEach(this.formEls_, function(formEl) {
    if (formEl.type == 'checkbox' && formEl.checked) {
      if (formEl.value != esn.constants.AnswerType.OTHER) {
        values.push(formEl.value);
      }
    } else if (formEl.type == 'text' && !goog.string.isEmpty(formEl.value)) {
      values.push(esn.constants.OTHER_VALUE_PREFIX +
          goog.string.trim(formEl.value));
    }
  }, this);
  return values;
};

esn.Question.prototype.getOtherInputEl_ = function() {
  // Only RADIO & CHECKBOX questions have Other fields.
  if (this.model_.getType() != esn.constants.QuestionType.CHECKBOX &&
      this.model_.getType() != esn.constants.QuestionType.RADIO) {
    return null;
  };
  return goog.array.find(this.formEls_, function(formEl) {
    return formEl.type == 'text';
  }, this);
};

esn.Question.prototype.getOtherRadioOrCheckbox_ = function() {
  // Only RADIO & CHECKBOX questions have Other fields.
  if (this.model_.getType() != esn.constants.QuestionType.CHECKBOX &&
      this.model_.getType() != esn.constants.QuestionType.RADIO) {
    return null;
  };
  return goog.array.find(this.formEls_, function(formEl) {
    return formEl.value == esn.constants.AnswerType.OTHER;
  }, this);
};

esn.Question.prototype.saveChanges_ = function(values) {
  // If the value hasn't changed we don't need to do anything.
  if (!this.answerDao_.getModel().valuesEqual(values)) {
    this.answerDao_.getModel().setValues(values);    
    if (values.length > 0) {
      this.answerDao_.save();
    } else {
      // There are no values so let's remove this answer from the db.
      this.answerDao_.remove(goog.bind(this.deleteAnswerResponse_, this));
    }

    // Remove "missing" class if question is answered (just in case it has one).
    if (this.isAnswered()) {
      goog.dom.classes.remove(this.getElement(), 'missing');
    }

    // If this question has an N/A checkbox then enable or disable it.
    if (this.notApplicableCheckbox_) {
      if (!this.notApplicableCheckbox_.checked && this.isAnswered()) {
        // A regular value is set (not N/A) so disable the N/A checkbox.
        this.notApplicableCheckbox_.disabled = true;
      } else if (!this.isAnswered()) {
        // No values are set so enable the N/A checkbox.
        this.notApplicableCheckbox_.disabled = false;
      }
    }

    this.dispatchEvent(esn.Question.EventType.CHANGE);
  }
};

esn.Question.prototype.deleteAnswerResponse_ = function(response) {
  if (!response.hasError()) {
    // Our answer was deleted so we need to create a new object (since a delete
    // DAO object is no longer valid).
    this.answerDao_ = new esn.dao.AnswerDao(
        new esn.model.AnswerModel(this.model_), this.surveyClient_);
  }
};
